{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true,
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "# Portfolio Optimizer\n",
    "\n",
    "The portfolio optimizer brings together the power of the Axioma Portfolio Optimizer with Marquee's risk analytics infrastructure\n",
    "to make minimizing your portfolio's factor risk possible within the same ecosystem.\n",
    "\n",
    "To use the optimizer, you must have a license to the Axioma Portfolio Optimizer. Please reach out to the\n",
    "[Marquee sales team](mailto:gs-marquee-sales@ny.email.gs.com?Subject=Portfolio Optimizer Trial Request)\n",
    "to learn more about how to get a license or how to bring an existing license to Marquee.\n",
    "\n",
    "## Step 1: Authenticate and Initialize Your Session\n",
    "\n",
    "First you will import the necessary modules and add your client id and client secret."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "import datetime as dt\n",
    "\n",
    "import pandas as pd\n",
    "from IPython.display import display\n",
    "\n",
    "from gs_quant.markets.optimizer import OptimizerUniverse, FactorConstraint, MaxFactorProportionOfRiskConstraint, \\\n",
    "    AssetConstraint, \\\n",
    "    SectorConstraint, OptimizerSettings, OptimizerStrategy, OptimizerConstraints, OptimizerObjective, OptimizerType\n",
    "from gs_quant.markets.position_set import Position, PositionSet\n",
    "from gs_quant.markets.securities import Asset, AssetIdentifier\n",
    "from gs_quant.models.risk_model import FactorRiskModel\n",
    "from gs_quant.session import GsSession, Environment\n",
    "from gs_quant.target.hedge import CorporateActionsTypes\n",
    "\n",
    "# Enter client id and secret\n",
    "GsSession.use(Environment.PROD, client_id=None, client_secret=None)\n",
    "\n",
    "print('GS Session initialized.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 2: Define Your Initial Position Set\n",
    "\n",
    "Use the `PositionSet` class in GS Quant to define the initial holdings to optimize.\n",
    "You can define your positions as a list of identifiers with quantities or, alternatively, as a\n",
    "list of identifiers with weights, along with a reference notional value.\n",
    "\n",
    "*GS Quant will resolve all identifiers (Bloomberg IDs, SEDOLs, RICs, etc) historically as of the optimization date.*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "position_set = PositionSet(\n",
    "    date=dt.date(day=23, month=9, year=2022),\n",
    "    positions=[\n",
    "        Position(identifier='AAPL UW', quantity=26),\n",
    "        Position(identifier='GS UN', quantity=51)\n",
    "    ]\n",
    ")\n",
    "\n",
    "position_set.resolve()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 3: Define Your Optimizer Universe\n",
    "\n",
    "An optimizer universe corresponds to the assets that can be used when constructing an optimization, which can be created\n",
    "using the `OptimizerUniverse` class:\n",
    "\n",
    "| Parameter | Description | Type| Default Value|\n",
    "|-----------------|---------------|-------------|-------------\n",
    "| `assets`      | Assets to include in the universe. |`List[Asset]`| N/A |\n",
    "| `explode_composites`     | Explode indices, ETFs, and baskets and include their underliers in the universe. |`boolean`| `True` |\n",
    "| `exclude_initial_position_set_assets`       | Exclude assets in the initial holdings from the optimization. | `boolean` | `False` |\n",
    "| `exclude_corporate_actions_types`     | Set of of corporate actions to be excluded in the universe. |`List[CorporateActionsTypes]`| `[]` |\n",
    "| `exclude_hard_to_borrow_assets`       | Exclude hard to borrow assets from the universe. | `boolean` | `False` |\n",
    "| `exclude_restricted_assets`       | Exclude assets on restricted trading lists from the universe. | `float` | `False` |\n",
    "| `min_market_cap`       | Lowest market cap allowed for any universe constituent. | `float` | `None` |\n",
    "| `max_market_cap`       | Highest market cap allowed for any universe constituent. | `float` | `None` |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "universe = OptimizerUniverse(\n",
    "    assets=[Asset.get('SPX', AssetIdentifier.BLOOMBERG_ID)],\n",
    "    explode_composites=True,\n",
    "    exclude_corporate_actions_types=[CorporateActionsTypes.Mergers]\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4: Define Your Risk Model and Factor Risk Constraints\n",
    "\n",
    "You can run the optimizer using a factor risk model of your choice, so long as you have a license for it, by leveraging\n",
    "the `FactorRiskModel` class. For any factor in the risk model, you can set more granular constraints on the optimized\n",
    "portfolio's exposure to the factor.\n",
    "\n",
    "If you'd like to limit your optimized portfolio's factor proportion of risk,\n",
    "you can do so with the `MaxFactorProportionOfRiskConstraint` class. It is also possible to create a bespoke list of\n",
    "factors and set their collective maximum proportion of risk using the `MaxProportionOfRiskByGroupConstraint` class.\n",
    "\n",
    "In this example, let's use the Axioma AXUS4S model and limit the final exposure to Volatility be $10,000 and\n",
    "the final exposure of Market Sensitivity to be 5,000. Let's also set a constraint so that the final positions don't have\n",
    "more than 30% of its total risk to be factor risk."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "risk_model = FactorRiskModel.get('AXIOMA_AXUS4S')\n",
    "\n",
    "factor_constraints = [\n",
    "    FactorConstraint(risk_model.get_factor('Volatility'), 10000),\n",
    "    FactorConstraint(risk_model.get_factor('Market Sensitivity'), 5000)\n",
    "]\n",
    "\n",
    "prop_of_risk_constraint = MaxFactorProportionOfRiskConstraint(30)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 5: Define Other Optimization Constraints\n",
    "\n",
    "Outside factor-specific constraints, it's also possible to limit the holding value of individual assets, assets\n",
    "belonging to a particular GICS sector, and/or assets in a particular country of domicile in the optimization.\n",
    "\n",
    "In this example, let's constrain the optimization to have 0-5% Microsoft and Twitter and limit the optimization's notional\n",
    "coming from Energy and Health Care assets to each be 0-30%."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "asset_constraints = [\n",
    "    AssetConstraint(Asset.get('MSFT UW', AssetIdentifier.BLOOMBERG_ID), 0, 5),\n",
    "    AssetConstraint(Asset.get('TWTR UN', AssetIdentifier.BLOOMBERG_ID), 0, 5)\n",
    "]\n",
    "\n",
    "sector_constraints = [\n",
    "    SectorConstraint('Energy', 0, 30),\n",
    "    SectorConstraint('Health Care', 0, 30)\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 6: Configure Your Optimization Settings\n",
    "\n",
    "All other settings for the optimization can be set via the `OptimizerSettings` class:\n",
    "\n",
    "| Parameter          | Description | Type| Default Value|\n",
    "|--------------------|---------------|-------------|-------------\n",
    "| `notional`         | Max gross notional value of the optimization |`float`| `10000000` |\n",
    "| `allow_long_short` | Allow a long/short optimization |`boolean`| `False` |\n",
    "| `min_names`        | Minimum number of assets in the optimization |`float`| `0` |\n",
    "| `max_names`        | Maximum number of assets in the optimization |`float`| `100` |\n",
    "| `min_weight_per_constituent`       | Minimum weight of each constituent in the optimization |`float`| `None` |\n",
    "| `max_weight_per_constituent`       | Maximum weight of each constituent in the optimization |`float`| `None` |\n",
    "| `max_adv`        | Maximum average daily volume of each constituent in the optimization (in percent) |`float`| `15` |\n",
    "| `constraint_priorities`        | Priorities to relax some or all constraints in the strategy |`ConstraintPriorities`| `None` |\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "settings = OptimizerSettings(allow_long_short=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 7: Create and Run a Strategy\n",
    "\n",
    "It's finally time to take all these parameters and construct an optimizer strategy using the `OptimizerStrategy` class:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": true,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "constraints = OptimizerConstraints(\n",
    "    asset_constraints=asset_constraints,\n",
    "    sector_constraints=sector_constraints,\n",
    "    factor_constraints=factor_constraints,\n",
    "    max_factor_proportion_of_risk=prop_of_risk_constraint\n",
    ")\n",
    "\n",
    "strategy = OptimizerStrategy(\n",
    "    initial_position_set=position_set,\n",
    "    constraints=constraints,\n",
    "    settings=settings,\n",
    "    universe=universe,\n",
    "    risk_model=risk_model,\n",
    "    objective=OptimizerObjective.MINIMIZE_FACTOR_RISK\n",
    ")\n",
    "\n",
    "strategy.run(optimizer_type=OptimizerType.AXIOMA_PORTFOLIO_OPTIMIZER)\n",
    "\n",
    "optimization = strategy.get_optimization() # Returns just the optimization results as a PositionSet object\n",
    "optimized_position_set = strategy.get_optimized_position_set()\n",
    "\n",
    "print('OPTIIMZATION RESULTS')\n",
    "result = [{'Asset': p.identifier, 'Quantity': p.quantity} for p in optimization.positions]\n",
    "display(pd.DataFrame(result))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "## Step 8: Create a Basket or Portfolio With Your Results\n",
    "\n",
    "Now that you have a position set for your optimization and your optimized position set, you can upload either to a basket or\n",
    "portfolio by following the following tutorials:\n",
    "\n",
    "- [Create a Basket](https://nbviewer.org/github/goldmansachs/gs-quant/blob/master/gs_quant/documentation/06_baskets/tutorials/Basket%20Create.ipynb)\n",
    "- [Create a Portfolio](https://nbviewer.org/github/goldmansachs/gs-quant/blob/master/gs_quant/documentation/10_one_delta/scripts/portfolios/Create%20New%20Portfolio.ipynb)\n",
    "\n",
    "*Other questions? Reach out to the [Portfolio Analytics team](mailto:gs-marquee-analytics-support@gs.com)!*\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}